using System; 
using System.Collections.Generic;
using System.Text;
using System.Data.Linq;
 
namespace System.Data.Linq.SqlClient {
 
    /// <summary> 
    /// Converts expressions of type NText, Text, Image to NVarChar(MAX), VarChar(MAX), VarBinary(MAX)
    /// where necessary. This can only be done on SQL2005, so we add a SqlServerCompatibilityAnnotation 
    /// to the changed nodes.
    /// </summary>
    internal class LongTypeConverter {
        Visitor visitor; 

        internal LongTypeConverter(SqlFactory sql) { 
            this.visitor = new Visitor(sql); 
        }
 
        internal SqlNode AddConversions(SqlNode node, SqlNodeAnnotations annotations) {
            visitor.Annotations = annotations;
            return visitor.Visit(node);
        } 

        class Visitor : SqlVisitor { 
            SqlFactory sql; 
            SqlNodeAnnotations annotations;
 
            internal SqlNodeAnnotations Annotations {
                set { this.annotations = value; }
            }
 
            internal Visitor(SqlFactory sql) {
                this.sql = sql; 
            } 

            private SqlExpression ConvertToMax(SqlExpression expr, ProviderType newType) { 
                return sql.UnaryConvert(expr.ClrType, newType, expr, expr.SourceExpression);
            }

            // returns CONVERT(VARCHAR/NVARCHAR/VARBINARY(MAX), expr) if provType is one of Text, NText or Image 
            // otherwise just returns expr
            // changed is true if CONVERT(...(MAX),...) was added 
            private SqlExpression ConvertToMax(SqlExpression expr, out bool changed) { 
                changed = false;
                if (!expr.SqlType.IsLargeType) 
                    return expr;
                ProviderType newType = sql.TypeProvider.GetBestLargeType(expr.SqlType);
                changed = true;
                if (expr.SqlType != newType) { 
                    return ConvertToMax(expr, newType);
                } 
                changed = false; 
                return expr;
            } 

            private void ConvertColumnsToMax(SqlSelect select, out bool changed, out bool containsLongExpressions) {
                SqlRow row = select.Row;
                changed = false; 
                containsLongExpressions = false;
                foreach (SqlColumn col in row.Columns) { 
                    bool columnChanged; 
                    containsLongExpressions = containsLongExpressions || col.SqlType.IsLargeType;
                    col.Expression = ConvertToMax(col.Expression, out columnChanged); 
                    changed = changed || columnChanged;
                }
            }
 
            internal override SqlSelect VisitSelect(SqlSelect select) {
                if (select.IsDistinct) { 
                    bool changed; 
                    bool containsLongExpressions;
                    ConvertColumnsToMax(select, out changed, out containsLongExpressions); 
                    if (containsLongExpressions) {
                        this.annotations.Add(select, new SqlServerCompatibilityAnnotation(
                                             string.Format(Strings.TextNTextAndImageCannotOccurInDistinct,select.SourceExpression), SqlProvider.ProviderMode.Sql2000, SqlProvider.ProviderMode.SqlCE));
                    } 

                } 
                return base.VisitSelect(select); 
            }
 
            internal override SqlNode VisitUnion(SqlUnion su) {
                bool changedLeft = false;
                bool containsLongExpressionsLeft = false;
                SqlSelect left = su.Left as SqlSelect; 
                if (left != null) {
                    ConvertColumnsToMax(left, out changedLeft, out containsLongExpressionsLeft); 
                } 
                bool changedRight = false;
                bool containsLongExpressionsRight = false; 
                SqlSelect right = su.Right as SqlSelect;
                if (right != null) {
                    ConvertColumnsToMax(right, out changedRight, out containsLongExpressionsRight);
                } 
                if (!su.All && (containsLongExpressionsLeft || containsLongExpressionsRight)) {
                    // unless the UNION is 'ALL', the server will perform a DISTINCT operation, 
                    // which isn't valid for large types (text, ntext, image) 
                    this.annotations.Add(su, new SqlServerCompatibilityAnnotation(
                        string.Format(Strings.TextNTextAndImageCannotOccurInUnion,su.SourceExpression), SqlProvider.ProviderMode.Sql2000, SqlProvider.ProviderMode.SqlCE)); 
                }
                return base.VisitUnion(su);
            }
 
            internal override SqlExpression VisitFunctionCall(SqlFunctionCall fc) {
                if (fc.Name == "LEN") { 
                    bool changed; 
                    fc.Arguments[0] = ConvertToMax(fc.Arguments[0],out changed);
                    if (fc.Arguments[0].SqlType.IsLargeType) { 
                        this.annotations.Add(fc, new SqlServerCompatibilityAnnotation(
                                                   string.Format(Strings.LenOfTextOrNTextNotSupported,fc.SourceExpression), SqlProvider.ProviderMode.Sql2000));
                    }
                } 
                return base.VisitFunctionCall(fc);
            } 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.